//public class seata {
//    跨服务调用，模块A1 调用模块B1接口
//
//    场景1：循环跨服务调用
//
//    模块A代码demo：
//
//    @Service
//    public class TestController{
//        @Resource
//        private TestService testService;
//
//        //开始测试的入口方法
//        public void test(...) {
//            for (int i = 0; i< 3;i++){
//                testService.call(...);
//            }
//        }
//
//        @Service
//        public class TestService {
//            @Resource
//            private Client client;
//            @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000)
//            @Transactional(rollbackFor = {Exception.class})
//            public Result<Boolean> call(...){
//                GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                System.out.println("xid is :" + seataInfo.getXid());
//                client.test(....)
//            }
//
//
//        }
//        模块B代码demo
//
//        @controller
//        public class Client{
//            public void test(...) {
//    .....
//                //insert数据库一条记录
//            }
//        }
//        测试结论：三次循环调用，每次的xid是不一样的
//
//        xid is :192.168.21.6:8091:9194228274031219
//        xid is :192.168.21.4:8091:9197040338104636
//        xid is :192.168.21.4:8091:9197040338104725
//
//        即每次调用都会起新的分布式事务。
//
//        将 @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000) 修改成? @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000,propagation = Propagation.REQUIRES_NEW)
//
//        测试结果不变，每次都是新的xid。
//
//
//
//        场景2：循环跨服务调用--其中第二次调用B模块接口异常。--异常是B抛出的
//
//        预期结果：第一次和第三次B模块数据入库，第二次B模块事务回滚。
//
//        实际结果：和预期结果一致
//
//        @Service
//        public class TestController{
//            @Resource
//            private TestService testService;
//
//            //开始测试的入口方法
//            public void test(...) {
//                for (int i = 0; i< 3;i++){
//                    testService.call(i);
//                }
//            }
//
//            @Service
//            public class TestService {
//                @Resource
//                private Client client;
//                @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000)
//                @Transactional(rollbackFor = {Exception.class})
//                public Result<Boolean> call(int i){
//                    GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                    System.out.println("xid is :" + seataInfo.getXid());
//                    try{
//                        client.test(i)//当i = 1的时候，被调用方抛异常
//                    }catch()
//
//                }
//
//
//            }
//
//
//            @controller
//            public class Client{
//                public void test(i) {
//                    if(i == 1){
//                        int o = i/0  //抛by zero异常
//                    }
//                    //insert数据库一条记录
//                }
//            }
//
//
//            测试结果： 第一次和第三次数据入库，第二次数据没入库。
//
//            结论：由于场景1种证明了每次循环分布式事务xid是不一样的，所以：B抛异常，回滚的是B的当前的分布式事务链路的逻辑
//
//
//
//            场景3：循环跨服务调用--其中调用第二次B模块接口后，A抛异常。--异常是A抛出的
//
//
//
//
//
//            @Service
//            public class TestController{
//                @Resource
//                private TestService testService;
//
//                //开始测试的入口方法
//                public void test(...) {
//                    for (int i = 0; i< 3;i++){
//                        testService.call(i);
//                    }
//                }
//
//                @Service
//                public class TestService {
//                    @Resource
//                    private Client client;
//                    @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000,propagation = Propagation.REQUIRES_NEW)
//                    @Transactional(rollbackFor = {Exception.class})
//                    public Result<Boolean> call(int i){
//                        GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                        System.out.println("xid is :" + seataInfo.getXid());
//                        client.test(i)//当i = 1的时候，被调用方抛异常
//                        try{
//                            if (i == 1){
//                                int o = i/0 //抛by zero异常,
//                            }
//                        } catch (Exception e){
//                            //本地服务抛出的异常，捕获异常。让程序继续走
//                            System.out.println(JSON.toJSONString(e));
//                        }
//                    }
//
//
//                }
//
//
//                @controller
//                public class Client{
//                    public void test(i) {
//     ....
//                        //insert数据库一条记录
//                    }
//                }
//                结果：数据库入了3条记录，第二条虽然A抛了异常，但是B模块的数据还是入了库。事务没回滚。。。第二条数据入库的时候，由于A 把异常try-catch起来了，所以分布式事务是提交了，B的数据是不会回滚的。
//
//
//
//                场景4：单次跨服务调用--先A数据入库，A→ B， B抛异常， A不try-catch
//
//                @Service
//                public class TestController{
//                    @Resource
//                    private TestService testService;
//
//                    //开始测试的入口方法
//                    public void test(...) {
//                        testService.call(..);
//
//                    }
//
//                    @Service
//                    public class TestService {
//                        @Resource
//                        private Client client;
//                        @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000)
//                        @Transactional(rollbackFor = {Exception.class})
//                        public Result<Boolean> call(int i){
//                            GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                            System.out.println("xid is :" + seataInfo.getXid());
//                            //insert一条数据到数据库
//    ....insert..
//                            //开始调用B的接口
//                            client.test()
//                        }
//
//
//                    }
//
//
//
//                    @controller
//                    public class Client{
//                        public void test(i) {
//                            //insert数据库一条记录
//                            int o = i/0  //抛by zero异常
//                        }
//                    }
//                    结论：B模块接口异常，导致A的数据也回滚了，B的也回滚了。整个调用链上的数据都会回滚。
//
//
//
//                    场景5：单次跨服务调用--先A数据入库，A→ B， 调用完成后A抛异常， A不try-catch
//
//                    @Service
//                    public class TestController{
//                        @Resource
//                        private TestService testService;
//
//                        //开始测试的入口方法
//                        public void test(...) {
//                            testService.call(..);
//
//                        }
//
//                        @Service
//                        public class TestService {
//                            @Resource
//                            private Client client;
//                            @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000)
//                            @Transactional(rollbackFor = {Exception.class})
//                            public Result<Boolean> call(int i){
//                                GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                                System.out.println("xid is :" + seataInfo.getXid());
//                                //insert一条数据到数据库
//    ....insert..
//                                //开始调用B的接口
//                                client.test()
//                                //抛异常
//                                int o = i/0 //抛by zero异常
//                            }
//
//
//                        }
//
//
//
//                        @controller
//                        public class Client{
//                            public void test(i) {
//                                //insert数据库一条记录
//
//                            }
//                        }
//                        结论：真个链路的事务回滚，和场景4一样。?
//
//                        故只要调用链上发生异常，分布式事务都会回滚。
//
//                        场景4和5，是分布式事务使用场景的基础，所以一定会回滚的。
//
//
//
//                        场景6：单次跨服务调用--内部服务链路出现多个分布式事务注解，是否会出现新的xid?
//
//                        eg： a1service.test1->a2service.test2
//
//                        @Service
//                        public class TestController{
//                            @Resource
//                            private TestService testService;
//
//                            //开始测试的入口方法
//                            @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000)
//                            @Transactional(rollbackFor = {Exception.class})
//                            public void test(...) {
//                                GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                                System.out.println("xid is :" + seataInfo.getXid())
//                                testService.call(..);
//                            }
//
//                            @Service
//                            public class TestService {
//                                @Resource
//                                private Client client;
//                                @GlobalTransactional(rollbackFor = Exception.class, timeoutMills = 300000)
//                                @Transactional(rollbackFor = {Exception.class})
//                                public Result<Boolean> call(int i){
//                                    GlobalTransaction seataInfo = GlobalTransactionContext.getCurrentOrCreate();
//                                    System.out.println("xid is :" + seataInfo.getXid());
//                                    //insert一条数据到数据库
//    ....insert..
//                                    //开始调用B的接口
//                                    client.test()
//                                    //抛异常
//                                    int o = i/0 //抛by zero异常
//                                }
//
//                            }
//
//                            总结：不会出现新的xid,沿用上游的分布式事务，本身的事务不会生效。两个xid是一样的。
//
//                            调用方A抛异常，被调用方B数据回滚，调用方A数据回滚了—--因为是一个分布式事务。
//
//
//
//                            备注：当将call方法的分布式事务注解新增propagation = Propagation.REQUIRES_NEW，则xid是null.? ?调用方A抛异常，被调用方B数据没有回滚，调用方A数据回滚了
//
//                            结论： 由于新的分布式事务为null,不存在事务，故被调用方B数据无法回滚。 但是存在本地事务，故A的数据能够回滚。
//
//                            故：综上几种场景，可以得出：别用propagation = Propagation.REQUIRES_NEW
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//                            threadlocal 变量没有remove是否会有影响的验证：
//
//                            验证代码：
//
//                            public static void main(String[] args) {
//                                ThreadLocal<String> stringtest = new ThreadLocal<>();
//                                //使用单线程的线程池，保证下次执行任务的线程，是上次执行任务的线程。
//                                ExecutorService executorService = Executors.newSingleThreadExecutor();
//                                for (int i = 0;i<5;i++){
//                                    executorService.submit(() -> {
//                                        try {
//                                            System.out.println("start result:" +stringtest.get());
//                                            stringtest.set("xxx" + UUID.randomUUID());//给变量赋值
//                                            System.out.println("end   result:" +stringtest.get());
//                                            System.out.println("----------------------");
//                                            Thread.sleep(1000);
//                                        } catch (InterruptedException e) {
//                                            e.printStackTrace();
//                                        }
//                                    });
//                                }
//                            }
//
//
//
//
//                            执行结果：
//                            start result:null
//                            end result:xxx55f818be-497d-494c-a573-d78ebb0ff228
//----------------------
//                            start result:xxx55f818be-497d-494c-a573-d78ebb0ff228
//                            end result:xxx073cc025-63dd-4ae5-8e52-64539734ed4d
//                                    ----------------------
//                            start result:xxx073cc025-63dd-4ae5-8e52-64539734ed4d
//                            end result:xxx0c3836b4-e640-4978-83ed-5712ce5c56b1
//----------------------
//                            start result:xxx0c3836b4-e640-4978-83ed-5712ce5c56b1
//                            end result:xxxfdffca45-e97a-47ab-a69a-a4cff0dde2ab
//----------------------
//                            start result:xxxfdffca45-e97a-47ab-a69a-a4cff0dde2ab
//                            end result:xxx748ac547-f843-421a-a6d7-6817ca1efe1b
//
//                            结果：每一次的start的值，都是上一次的end的值。?
//
//
//
//                            当对threadlocal进行remove操作后：
//
//                            demo code:
//
//
//
//                            public static void main(String[] args) {
//                                ThreadLocal<String> stringtest = new ThreadLocal<>();
//                                //使用单线程的线程池，保证下次执行任务的线程，是上次执行任务的线程。
//                                ExecutorService executorService = Executors.newSingleThreadExecutor();
//                                for (int i = 0;i<5;i++){
//                                    executorService.submit(() -> {
//                                        try {
//                                            System.out.println("start result:" +stringtest.get());
//                                            stringtest.set("xxx" + UUID.randomUUID());//给变量赋值
//                                            System.out.println("end   result:" +stringtest.get());
//                                            System.out.println("----------------------");
//                                            stringtest.remove();
//                                            Thread.sleep(1000);
//                                        } catch (InterruptedException e) {
//                                            e.printStackTrace();
//                                        }
//                                    });
//                                }
//                            }
//
//
////输出结果
//
//                            start result:null
//                            end result:xxx89181063-2818-49d6-a87d-060b290986b6
//----------------------
//                            start result:null
//                            end result:xxxeccaf7b0-8d35-4318-a761-1de72d7f08f2
//----------------------
//                            start result:null
//                            end result:xxx74bcec90-29c8-4548-93ea-8d3c4730a1b6
//----------------------
//                            start result:null
//                            end result:xxxdf95f2dc-4e49-4283-813a-c2a8d11b4a10
//----------------------
//                            start result:null
//                            end result:xxxc4c03dbd-b610-4dff-9b07-8f4a55645a49
//
//
//
//                            故：threadlocal用完就要remove. 除非说每次都set覆盖也可以。? 要避免没有clean,但是新的线程没有set，直接取get。
//
//                            或者：在线程开始的地方remove,也可以避免受到影响：
//
//                            demo code:
//
//                            public static void main(String[] args) {
//                                ThreadLocal<String> stringtest = new ThreadLocal<>();
//                                //使用单线程的线程池，保证下次执行任务的线程，是上次执行任务的线程。
//                                ExecutorService executorService = Executors.newSingleThreadExecutor();
//                                for (int i = 0;i<5;i++){
//                                    executorService.submit(() -> {
//                                        try {
//                                            stringtest.remove();
//                                            System.out.println("start result:" +stringtest.get());
//                                            stringtest.set("xxx" + UUID.randomUUID());//给变量赋值
//                                            System.out.println("end   result:" +stringtest.get());
//                                            System.out.println("----------------------");
//                                            Thread.sleep(1000);
//                                        } catch (InterruptedException e) {
//                                            e.printStackTrace();
//                                        }
//                                    });
//                                }
//                            }
//
//                            //输出结果：
//                            start result:null
//                            end result:xxx2c944509-4414-4d1f-b54f-463103344e90
//                                    ----------------------
//                            start result:null
//                            end result:xxxe33388b1-4b43-4fec-8da0-932edfc59929
//----------------------
//                            start result:null
//                            end result:xxxfd95ce5b-d92c-453e-9442-b6115f0d9298
//----------------------
//                            start result:null
//                            end result:xxx7f8c01fd-9a3d-4365-8776-ad12205cf684
//----------------------
//                            start result:null
//                            end result:xxx7020dfaa-ff0c-440f-8bd5-6a2b1afd4cef
//----------------------
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//                        }
